using Kopernikus.Collections.Generic;

namespace Kopernikus.Parsing.GrammarSets
{
    public class NullableSymbolsAndProductions
    {
        private readonly IGrammar m_grammar;
        private Set<Production> m_nullableProductions;
        private Set<Symbol> m_nullableSymbols;

        public NullableSymbolsAndProductions(IGrammar grammar)
        {
            m_grammar = grammar;
        }

        public IGrammar Grammar
        {
            get { return m_grammar; }
        }

        public Set<Symbol> Symbols
        {
            get
            {
                Calculate();
                return m_nullableSymbols;
            }
        }

        public Set<Production> Productions
        {
            get
            {
                Calculate();
                return m_nullableProductions;
            }
        }

        private void Calculate()
        {
            if (m_nullableSymbols == null)
            {
                m_nullableSymbols = new Set<Symbol>();
                m_nullableProductions = new Set<Production>();

                while (true)
                {
                    int symbolCount = m_nullableSymbols.Count;
                    int productionCount = m_nullableProductions.Count;

                    foreach (Production p in Grammar.Productions)
                    {
                        bool isNullable = true;
                        foreach (Symbol s in p.RHS)
                        {
                            if (!m_nullableSymbols.Contains(s))
                            {
                                isNullable = false;
                                break;
                            }
                        }
                        if (isNullable)
                        {
                            m_nullableSymbols.Add(p.LHS);
                            m_nullableProductions.Add(p);
                        }
                    }
                    if ((symbolCount == m_nullableSymbols.Count) && (productionCount == m_nullableProductions.Count))
                    {
                        break;
                    }
                }
            }
        }
    }
}